{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit RadioTuner;

interface

uses Windows;

const
  USB_RADIO_DLL_NAME = 'USBRadio.dll';
  TUNING_INTERVAL = 100;
  MAX_SIGNAL_STRENGTH = 100;

type
  TAHMHWInit = function(): Boolean; stdcall;
  TAHMHWInitEx = function(enableSound: Boolean): Boolean; stdcall;
  TAHMHWDeInit = function(): Boolean; stdcall;
  TAHMFMTune = function(frequency: LongInt): Boolean; stdcall;
  TAHMGetSignal = function(): LongInt; stdcall;
  TAHMGetCurrStation = function(): LongInt; stdcall;
  TAHMSeekStation = function(SeekUp: Boolean): LongInt; stdcall;
  TAHMIsStereo = function(): Boolean; stdcall;
  TAHMGetRDS = function(): PChar; stdcall;
  TAHMScanStation = function(directionUpDown: Boolean; startFrequency: LongInt): LongInt; stdcall;
  TAHMFMTuneUp = function(): Boolean; stdcall;
  TAHMFMTuneDown = function(): Boolean; stdcall;
  TAHMGetModuleName = function(): PChar; stdcall;
  TAHMGetModuleInfo = function(): LongInt; stdcall;
  TAHMTuneFreq = procedure(frequency: LongInt); stdcall;
  TAHMSetMute = procedure(mute: Boolean); stdcall;
  TAHMGetVolume = function(): LongInt; stdcall;
  TAHMSetVolume = procedure(left, right: LongInt); stdcall;
  TAHMVolumeUpDown = procedure(step: LongInt); stdcall;
  TAHMVB_GetRDSText = function(buffy: PChar; length: PWord): Boolean; stdcall;
  TAHMVB_GetRDSPS = function(buffy: PChar; length: PWord): Boolean; stdcall;
  TAHMVB_GetRDSPIRegion = function(buffy: PChar; length: PWord): Boolean; stdcall;
  TAHMVB_GetRDSPICountry = function(buffy: PChar; length: PWord): Boolean; stdcall;
  TAHMVB_GetRDSPTYString = function(buffy: PChar; length: PWord): Boolean; stdcall;

type
  TAHMRadioTuner = class
  private
    FEnabled: Boolean;
  protected
    function GetReady: Boolean; virtual;
    function GetName: String; virtual;
    function GetVersion: String; virtual;
    function GetDescription: String; virtual;
    procedure SetEnabled(Enabled: Boolean);
    function GetFrequency: LongInt; virtual;
    procedure SetFrequency(Frequency: LongInt); virtual;
    function GetSignalStrength: Integer; virtual;
    function GetIsStereo: Boolean; virtual;
    function GetRDSText: String; virtual;
    function GetRDSStation: String; virtual;
    function GetRDSRegion: String; virtual;
    function GetRDSCountry: String; virtual;
    function GetRDSProgramType: String; virtual;
    function Open: Boolean; virtual;
    procedure Close; virtual;
  public
    constructor Create;
    property Name: String read GetName;
    property Version: String read GetVersion;
    property Description: String read GetDescription;
    property Ready: Boolean read GetReady;
    property Frequency: LongInt read GetFrequency write SetFrequency;
    property SignalStrength: Integer read GetSignalStrength;
    property IsStereo: Boolean read GetIsStereo;
    property RDSText: String read GetRDSText;
    property RDSStation: String read GetRDSStation;
    property RDSRegion: String read GetRDSRegion;
    property RDSCountry: String read GetRDSCountry;
    property RDSProgramType: String read GetRDSProgramType;
    property Enabled: Boolean read FEnabled write SetEnabled;
    procedure Reset; virtual;
    procedure SeekUp; virtual;
    procedure SeekDown; virtual;
    procedure TuneUp;
    procedure TuneDown;
  end;

  TAHMDLLRadioTuner = class(TAHMRadioTuner)
  private
    FDLLFailed: Boolean;
    FDLLName: String;
    FDLLHandle: Integer;
  protected
    function GetReady: Boolean; override;
    function CheckDependencies: Boolean; virtual;
    procedure LoadDLL;
    procedure ConfigureDLL; virtual;
    procedure CleanupDLL; virtual;
    procedure UnloadDLL;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Reset; override;
    property DLLName: String read FDLLName write FDLLName;
  end;

  TAHMUSBRadioDLLTuner = class(TAHMDLLRadioTuner)
  private
    FHWInit: TAHMHWInit;
    FHWInitEx: TAHMHWInitEx;
    FHWDeInit: TAHMHWDeInit;
    FFMTune: TAHMFMTune;
    FGetSignal: TAHMGetSignal;
    FGetCurrStation: TAHMGetCurrStation;
    FSeekStation: TAHMSeekStation;
    FIsStereo: TAHMIsStereo;
    FGetRDS: TAHMGetRDS;
    FScanStation: TAHMScanStation;
    FFMTuneUp: TAHMFMTuneUp;
    FFMTuneDown: TAHMFMTuneDown;
    FGetModuleName: TAHMGetModuleName;
    FGetModuleInfo: TAHMGetModuleInfo;
    FTuneFreq: TAHMTuneFreq;
    FSetMute: TAHMSetMute;
    FGetVolume: TAHMGetVolume;
    FSetVolume: TAHMSetVolume;
    FVolumeUpDown: TAHMVolumeUpDown;
    FVB_GetRDSText: TAHMVB_GetRDSText;
    FVB_GetRDSPS: TAHMVB_GetRDSPS;
    FVB_GetRDSPIRegion: TAHMVB_GetRDSPIRegion;
    FVB_GetRDSPICountry: TAHMVB_GetRDSPICountry;
    FVB_GetRDSPTYString: TAHMVB_GetRDSPTYString;
  protected
    function GetName: String; override;
    function GetDescription: String; override;
    function GetFrequency: LongInt; override;
    procedure SetFrequency(Frequency: LongInt); override;
    function GetSignalStrength: Integer; override;
    function GetIsStereo: Boolean; override;
    function GetRDSText: String; override;
    function GetRDSStation: String; override;
    function GetRDSRegion: String; override;
    function GetRDSCountry: String; override;
    function GetRDSProgramType: String; override;
    function Open: Boolean; override;
    procedure Close; override;
    procedure ConfigureDLL; override;
    procedure CleanupDLL; override;
  public
    constructor Create;
    procedure SeekUp; override;
    procedure SeekDown; override;
  end;


implementation

// TAHMRadioTuner

constructor TAHMRadioTuner.Create;
begin
  inherited Create;

  FEnabled := False;
end;

function TAHMRadioTuner.GetReady: Boolean;
begin
  // Descendants may implement this if required
  Result := True;
end;

function TAHMRadioTuner.GetName: String;
begin
  // Descendants may implement this if required
  Result := '';
end;

function TAHMRadioTuner.GetVersion: String;
begin
  // Descendants may implement this if required
  Result := '';
end;

function TAHMRadioTuner.GetDescription: String;
begin
  // Descendants may implement this if required
  Result := '';
end;

procedure TAHMRadioTuner.SetEnabled(Enabled: Boolean);
begin
  if Enabled <> FEnabled then
  begin
    if Enabled then
    begin
      // Initialise
      if Open then FEnabled := True;
    end
    else
    begin
      // Shutdown
      Close;
      FEnabled := False;
    end;
  end;
end;

function TAHMRadioTuner.GetFrequency: LongInt;
begin
  // Descendants will implement this method
  Result := 0;
end;

procedure TAHMRadioTuner.SetFrequency(Frequency: LongInt);
begin
  // Descendants will implement this method
end;

function TAHMRadioTuner.GetSignalStrength: Integer;
begin
  // Descendants will implement this method
  Result := 0;
end;

function TAHMRadioTuner.GetIsStereo: Boolean;
begin
  // Descendants will implement this method
  Result := True;
end;

function TAHMRadioTuner.GetRDSText: String;
begin
  // Descendants will implement this method
  Result := '';
end;

function TAHMRadioTuner.GetRDSStation: String;
begin
  // Descendants will implement this method
  Result := '';
end;

function TAHMRadioTuner.GetRDSRegion: String;
begin
  // Descendants will implement this method
  Result := '';
end;

function TAHMRadioTuner.GetRDSCountry: String;
begin
  // Descendants will implement this method
  Result := '';
end;

function TAHMRadioTuner.GetRDSProgramType: String;
begin
  // Descendants will implement this method
  Result := '';
end;

function TAHMRadioTuner.Open: Boolean;
begin
  // Descendants may implement this if required
  Result := True;
end;

procedure TAHMRadioTuner.Close;
begin
  // Descendants may implement this if required
end;

procedure TAHMRadioTuner.Reset;
begin
  SetEnabled(False);
  SetEnabled(True);
end;

procedure TAHMRadioTuner.SeekUp;
begin
  // Descendants may implement this if required
end;

procedure TAHMRadioTuner.SeekDown;
begin
  // Descendants may implement this if required
end;

procedure TAHMRadioTuner.TuneUp;
begin
  // Increase tuning freqency
  Frequency := Frequency + TUNING_INTERVAL;
end;

procedure TAHMRadioTuner.TuneDown;
begin
  // Decrease tuning freqency
  Frequency := Frequency - TUNING_INTERVAL;
end;


// TAHMDLLRadioTuner

constructor TAHMDLLRadioTuner.Create;
begin
  inherited Create;

  // Initialise nil DLL method pointers
  UnloadDLL;
end;

destructor TAHMDLLRadioTuner.Destroy;
begin
  // Shutdown DLL cleanly
  SetEnabled(False);
  UnloadDLL;

  inherited Destroy;
end;

function TAHMDLLRadioTuner.GetReady: Boolean;
begin
  // Set default result - not ready yet
  Result := False;

  // Don't attempt to load if previously failed
  if FDLLFailed then Exit;

  if CheckDependencies then
  begin
    // Load DLL and check for success
    LoadDLL;
    Result := FDLLHandle <> 0;
  end;

  // If we're not ready then disable DLL
  if not Result then SetEnabled(False);
end;

function TAHMDLLRadioTuner.CheckDependencies: Boolean;
begin
  // Descendants may implement this to determine any dependencies
  Result := True;
end;

procedure TAHMDLLRadioTuner.LoadDLL;
begin
  // Don't attempt to load DLL if already loaded
  if FDLLHandle <> 0 then Exit;

  // Attempt to load the required dll
  FDLLHandle := LoadLibrary(PAnsiChar(FDLLName));
  if FDLLHandle <> 0 then ConfigureDLL
                     else FDLLFailed := True;
end;

procedure TAHMDLLRadioTuner.ConfigureDLL;
begin
  // Descendants may implement this to lookup DLL methods
end;

procedure TAHMDLLRadioTuner.CleanupDLL;
begin
  // Descendants may implement this to disconnect DLL methods
end;

procedure TAHMDLLRadioTuner.UnloadDLL;
begin
  CleanupDLL;

  if FDLLHandle <> 0 then FreeLibrary(FDLLHandle);
  FDLLHandle := 0;
  FDLLFailed := False;
end;

procedure TAHMDLLRadioTuner.Reset;
begin
  // Force reset & reloading of DLL
  SetEnabled(False);
  UnloadDLL;
  LoadDLL;
  SetEnabled(True);
end;

// TAHMUSBRadioDLLTuner

constructor TAHMUSBRadioDLLTuner.Create;
begin
  inherited;

  DLLName := USB_RADIO_DLL_NAME;
end;

function TAHMUSBRadioDLLTuner.GetName: String;
begin
  Result := USB_RADIO_DLL_NAME;
end;

function TAHMUSBRadioDLLTuner.GetDescription: String;
begin
  if @FGetModuleName <> nil then
    Result := FGetModuleName
  else
    Result := '';
end;

function TAHMUSBRadioDLLTuner.GetFrequency: LongInt;
begin
  if @FGetCurrStation <> nil then Result := FGetCurrStation
                             else Result := 0;
end;

procedure TAHMUSBRadioDLLTuner.SetFrequency(Frequency: LongInt);
begin
  if @FFMTune <> nil then FFMTune(Frequency);
end;

function TAHMUSBRadioDLLTuner.GetSignalStrength: Integer;
begin
  // Map signal strength to our percentage range
  if @FGetSignal <> nil then Result := Round(FGetSignal / 63 * MAX_SIGNAL_STRENGTH)
                        else Result := 0;
end;

function TAHMUSBRadioDLLTuner.GetIsStereo: Boolean;
begin
  if @FIsStereo <> nil then Result := FIsStereo
                       else Result := True;
end;

function TAHMUSBRadioDLLTuner.GetRDSText: String;
var
  Buffy: array[0..255] of Char;
  Length: Word;
begin
  if (@FVB_GetRDSText <> nil) and FVB_GetRDSText(@Buffy, @Length) then
    Result := Copy(Buffy, 1, Length)
  else
    Result := '';
end;

function TAHMUSBRadioDLLTuner.GetRDSStation: String;
var
  Buffy: array[0..7] of Char;
  Length: Word;
begin
  if (@FVB_GetRDSPS <> nil) and FVB_GetRDSPS(@Buffy, @Length) then
    Result := Copy(Buffy, 1, Length)
  else
    Result := '';
end;

function TAHMUSBRadioDLLTuner.GetRDSRegion: String;
var
  Buffy: array[0..255] of Char;
  Length: Word;
begin
  if (@FVB_GetRDSPIRegion <> nil) and FVB_GetRDSPIRegion(@Buffy, @Length) then
    Result := Copy(Buffy, 1, Length)
  else
    Result := '';
end;

function TAHMUSBRadioDLLTuner.GetRDSCountry: String;
var
  Buffy: array[0..255] of Char;
  Length: Word;
begin
  if (@FVB_GetRDSPICountry <> nil) and FVB_GetRDSPICountry(@Buffy, @Length) then
  begin
    Result := Copy(Buffy, 1, Length);

    // Ignore multiple choice countries
    if Pos('/', Result) > 0 then Result := '';
  end
  else
    Result := '';
end;

function TAHMUSBRadioDLLTuner.GetRDSProgramType: String;
var
  Buffy: array[0..255] of Char;
  Length: Word;
begin
  if (@FVB_GetRDSPTYString <> nil) and FVB_GetRDSPTYString(@Buffy, @Length) then
    Result := Copy(Buffy, 1, Length)
  else
    Result := '';
end;

function TAHMUSBRadioDLLTuner.Open: Boolean;
begin
  if @FHWInitEx <> nil then
    Result := FHWInitEx(False)
  else
    Result := False;
end;

procedure TAHMUSBRadioDLLTuner.Close;
begin
  // Close DLL
  if @FHWDeInit <> nil then FHWDeInit;
end;

procedure TAHMUSBRadioDLLTuner.SeekUp;
begin
  if @FSeekStation <> nil then FSeekStation(True);
end;

procedure TAHMUSBRadioDLLTuner.SeekDown;
begin
  if @FSeekStation <> nil then FSeekStation(False);
end;

procedure TAHMUSBRadioDLLTuner.ConfigureDLL;
begin
  inherited;

  // Get process address of interesting DLL methods
  @FHWInit             := GetProcAddress(FDLLHandle, 'HWInit');
  @FHWInitEx           := GetProcAddress(FDLLHandle, 'HWInitEx');
  @FHWDeInit           := GetProcAddress(FDLLHandle, 'HWDeInit');
  @FFMTune             := GetProcAddress(FDLLHandle, 'FMTune');
  @FGetSignal          := GetProcAddress(FDLLHandle, 'GetSignal');
  @FGetCurrStation     := GetProcAddress(FDLLHandle, 'GetCurrStation');
  @FSeekStation         := GetProcAddress(FDLLHandle, 'SeekStation');
  @FIsStereo           := GetProcAddress(FDLLHandle, 'IsStereo');
  @FGetRDS             := GetProcAddress(FDLLHandle, 'GetRDS');
  @FScanStation        := GetProcAddress(FDLLHandle, 'ScanStation');
  @FFMTuneUp           := GetProcAddress(FDLLHandle, 'FMTuneUp');
  @FFMTuneDown         := GetProcAddress(FDLLHandle, 'FMTuneDown');
  @FGetModuleName      := GetProcAddress(FDLLHandle, 'GetModuleName');
  @FGetModuleInfo      := GetProcAddress(FDLLHandle, 'GetModuleInfo');
  @FTuneFreq           := GetProcAddress(FDLLHandle, 'TuneFreq');
  @FSetMute            := GetProcAddress(FDLLHandle, 'SetMute');
  @FGetVolume          := GetProcAddress(FDLLHandle, 'GetVolume');
  @FSetVolume          := GetProcAddress(FDLLHandle, 'SetVolume');
  @FVolumeUpDown       := GetProcAddress(FDLLHandle, 'VolumeUpDown');
  @FVB_GetRDSText      := GetProcAddress(FDLLHandle, 'VB_GetRDSText');
  @FVB_GetRDSPS        := GetProcAddress(FDLLHandle, 'VB_GetRDSPS');
  @FVB_GetRDSPIRegion  := GetProcAddress(FDLLHandle, 'VB_GetRDSPIRegion');
  @FVB_GetRDSPICountry := GetProcAddress(FDLLHandle, 'VB_GetRDSPICountry');
  @FVB_GetRDSPTYString := GetProcAddress(FDLLHandle, 'VB_GetRDSPTYString');
end;

procedure TAHMUSBRadioDLLTuner.CleanupDLL;
begin
  inherited;

  // Remove stale DLL method pointers
  @FHWInit             := nil;
  @FHWInitEx           := nil;
  @FHWDeInit           := nil;
  @FFMTune             := nil;
  @FGetSignal          := nil;
  @FGetCurrStation     := nil;
  @FSeekStation        := nil;
  @FIsStereo           := nil;
  @FGetRDS             := nil;
  @FScanStation        := nil;
  @FFMTuneUp           := nil;
  @FFMTuneDown         := nil;
  @FGetModuleName      := nil;
  @FGetModuleInfo      := nil;
  @FTuneFreq           := nil;
  @FSetMute            := nil;
  @FGetVolume          := nil;
  @FSetVolume          := nil;
  @FVolumeUpDown       := nil;
  @FVB_GetRDSText      := nil;
  @FVB_GetRDSPS        := nil;
  @FVB_GetRDSPIRegion  := nil;
  @FVB_GetRDSPICountry := nil;
  @FVB_GetRDSPTYString := nil;
end;


end.
